package org.dromara.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.dromara.admin.domain.*;
import org.dromara.admin.domain.vo.OrderVo;
import org.dromara.admin.enums.AuditStatus;
import org.dromara.admin.enums.BillStatus;
import org.dromara.admin.enums.BillType;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.lessor.domain.House;
import org.dromara.system.domain.SysUser;
import org.springframework.stereotype.Service;
import org.dromara.admin.domain.bo.ContractBillBo;
import org.dromara.admin.domain.vo.ContractBillVo;
import org.dromara.admin.mapper.ContractBillMapper;
import org.dromara.admin.service.IContractBillService;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Collection;

/**
 * 租金账单信息Service业务层处理
 *
 * @author Lion Li
 * @date 2025-09-03
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class ContractBillServiceImpl implements IContractBillService {

    private final ContractBillMapper baseMapper;

    /**
     * 查询租金账单信息
     *
     * @param id 主键
     * @return 租金账单信息
     */
    @Override
    public ContractBillVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 联表查询ContractBill表、House、Order、ContractPayRecord、SysUser表
     */
    private static MPJLambdaWrapper<ContractBill> getOrderInfoByJoinTable(ContractBillBo bo) {
        // 使用MPJBaseMapper进行联表查询ContractBill表、House、Order、ContractPayRecord、SysUser表
        return new MPJLambdaWrapper<ContractBill>()
            .selectAll(ContractBill.class) // 订单表字段
            .selectAssociation(Order.class, ContractBillVo::getOrder)
            .selectAssociation(House.class, ContractBillVo::getHouse)
            .selectCollection(ContractPayRecord.class, ContractBillVo::getContractPayRecordList)
            .selectAssociation("tu", SysUser.class, ContractBillVo::getTenantUser)
            .selectAssociation("td", UserDetail.class, ContractBillVo::getTenantUserDetail)
            .selectAssociation("lu", SysUser.class, ContractBillVo::getLessorUser)
            .selectAssociation("cu", SysUser.class, ContractBillVo::getCapitalUser)
            .leftJoin(Order.class, Order::getId, ContractBill::getOrderId)
            .leftJoin(House.class, House::getId, Order::getHouseId)
            .leftJoin(ContractPayRecord.class, ContractPayRecord::getBillId, ContractBill::getId)
            // 租客信息 join
            .leftJoin(SysUser.class, "tu", on -> on.eq(SysUser::getUserId, ContractBill::getUserId))
            // 租客详细信息 join
            .leftJoin(UserDetail.class, "td", on -> on.eq(UserDetail::getUserId, ContractBill::getUserId))
            // 出租方信息 join
            .leftJoin(SysUser.class, "lu", on -> on.eq(SysUser::getUserId, ContractBill::getLessorUserId))
            // 资方代理信息 join
            .leftJoin(SysUser.class, "cu", on -> on.eq(SysUser::getUserId, ContractBill::getCapitalUserId))
            .like(StrUtil.isNotBlank(bo.getUserName()), "tu.user_name", bo.getUserName())
            .like(StrUtil.isNotBlank(bo.getLessorUserName()), "lu.user_name", bo.getLessorUserName())
            .like(StrUtil.isNotBlank(bo.getCapitalUserName()), "cu.user_name", bo.getCapitalUserName())
            .eq(bo.getUserId() != null, ContractBill::getUserId, bo.getUserId()) // 租客id
            .eq(bo.getLessorUserId() != null, ContractBill::getLessorUserId, bo.getLessorUserId()) // 房东id
            .eq(bo.getCapitalUserId() != null, ContractBill::getCapitalUserId, bo.getCapitalUserId()) // 资方id
            .eq(bo.getOrderId() != null, ContractBill::getOrderId, bo.getOrderId()) // 订单id
            .eq(StrUtil.isNotBlank(bo.getBillType()), ContractBill::getBillType, bo.getBillType()) // 账单类型
            .eq(StrUtil.isNotBlank(bo.getBillStatus()), ContractBill::getBillStatus, bo.getBillStatus()) // 账单状态
            .eq(StrUtil.isNotBlank(bo.getOverdue()), ContractBill::getOverdue, bo.getOverdue()); // 是否逾期
    }

    /**
     * 资方查询自己未来的收益
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return
     */
    @Override
    public TableDataInfo<ContractBillVo> queryCapitalFutureIncomeList(ContractBillBo bo, PageQuery pageQuery) {
        MPJLambdaWrapper<ContractBill> wrapper = getOrderInfoByJoinTable(bo);
        wrapper.eq(ContractBill::getBillStatus, BillStatus.UNPAID.getCode())
            .eq(ContractBill::getBillType, BillType.RENT.getCode());
        Page<ContractBillVo> result = baseMapper.selectJoinPage(pageQuery.build(), ContractBillVo.class, wrapper);
        return TableDataInfo.build(result);
    }

    /**
     * 后台获取租金账单信息列表(交易订单)
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 租金账单信息分页列表
     */
    @Override
    public TableDataInfo<ContractBillVo> queryPageBillList(ContractBillBo bo, PageQuery pageQuery) {
        MPJLambdaWrapper<ContractBill> wrapper = getOrderInfoByJoinTable(bo);
        Page<ContractBillVo> result = baseMapper.selectJoinPage(pageQuery.build(), ContractBillVo.class, wrapper);
        return TableDataInfo.build(result);
    }

    /**
     * 出租方获取自己租金账单信息列表(财务列表)
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 租金账单信息分页列表
     */
    @Override
    public TableDataInfo<ContractBillVo> queryPageLessorMyList(ContractBillBo bo, PageQuery pageQuery) {
        MPJLambdaWrapper<ContractBill> wrapper = getOrderInfoByJoinTable(bo);
        Page<ContractBillVo> result = baseMapper.selectJoinPage(pageQuery.build(), ContractBillVo.class, wrapper);
        return TableDataInfo.build(result);
    }

    /**
     * 分页查询租金账单信息列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 租金账单信息分页列表
     */
    @Override
    public TableDataInfo<ContractBillVo> queryPageList(ContractBillBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<ContractBill> lqw = buildQueryWrapper(bo);
        Page<ContractBillVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 分页查询当前登录人的租金账单信息列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return
     */
    @Override
    public TableDataInfo<ContractBillVo> queryPageMyList(ContractBillBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<ContractBill> lqw = buildQueryWrapper(bo);
        Page<ContractBillVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的租金账单信息列表
     *
     * @param bo 查询条件
     * @return 租金账单信息列表
     */
    @Override
    public List<ContractBillVo> queryList(ContractBillBo bo) {
        LambdaQueryWrapper<ContractBill> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<ContractBill> buildQueryWrapper(ContractBillBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<ContractBill> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(ContractBill::getIssueNumber);
        lqw.eq(StringUtils.isNotBlank(bo.getBillType()), ContractBill::getBillType, bo.getBillType());
        lqw.eq(bo.getBillAmount() != null, ContractBill::getBillAmount, bo.getBillAmount());
        lqw.eq(bo.getAmountReceivable() != null, ContractBill::getAmountReceivable, bo.getAmountReceivable());
        lqw.eq(bo.getAmountDiscount() != null, ContractBill::getAmountDiscount, bo.getAmountDiscount());
        lqw.eq(bo.getAmountReality() != null, ContractBill::getAmountReality, bo.getAmountReality());
        lqw.eq(bo.getAmountReceived() != null, ContractBill::getAmountReceived, bo.getAmountReceived());
        lqw.eq(StringUtils.isNotBlank(bo.getBillStatus()), ContractBill::getBillStatus, bo.getBillStatus());
        lqw.eq(bo.getPayTime() != null, ContractBill::getPayTime, bo.getPayTime());
        lqw.eq(bo.getExpiresTime() != null, ContractBill::getExpiresTime, bo.getExpiresTime());
        lqw.eq(StringUtils.isNotBlank(bo.getOverdue()), ContractBill::getOverdue, bo.getOverdue());
        lqw.eq(bo.getOrderId() != null, ContractBill::getOrderId, bo.getOrderId());
        lqw.eq(bo.getUserId() != null, ContractBill::getUserId, bo.getUserId());
        lqw.eq(bo.getLessorUserId() != null, ContractBill::getLessorUserId, bo.getLessorUserId());
        lqw.eq(bo.getCapitalUserId() != null, ContractBill::getCapitalUserId, bo.getCapitalUserId());
        lqw.eq(StringUtils.isNotBlank(bo.getRemarks()), ContractBill::getRemarks, bo.getRemarks());
        return lqw;
    }

    /**
     * 新增租金账单信息
     *
     * @param bo 租金账单信息
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(ContractBillBo bo) {
        ContractBill add = MapstructUtils.convert(bo, ContractBill.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改租金账单信息
     *
     * @param bo 租金账单信息
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(ContractBillBo bo) {
        ContractBill update = MapstructUtils.convert(bo, ContractBill.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(ContractBill entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除租金账单信息信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    /**
     * 创建押金账单
     *
     * @param order 订单信息
     * @return 是否创建成功
     */
    @Override
    public Boolean createDepositBill(Order order) {
        // 根据订单信息创建押金账单
        ContractBill contractBill = new ContractBill();
        contractBill.setBillType(BillType.PLEDGE.getCode());
        contractBill.setBillAmount(order.getDeposit());
        contractBill.setAmountReceivable(order.getDeposit());
        contractBill.setAmountDiscount(BigDecimal.ZERO);
        contractBill.setAmountReality(BigDecimal.ZERO);
        contractBill.setAmountReceived(BigDecimal.ZERO);
        contractBill.setBillStatus(BillStatus.UNPAID.getCode());
        contractBill.setOverdue(AuditStatus.N.getCode());
        contractBill.setOrderId(order.getId());
        contractBill.setUserId(order.getUserId());
        contractBill.setLessorUserId(order.getLessorUserId());
        return baseMapper.insert(contractBill) > 0;
    }

    /**
     * 创建租金账单
     *
     * @param order 订单信息
     * @return 是否创建成功
     */
    @Override
    public Boolean createRentBill(Order order) {
        // 根据订单中订单时长创建多少个租金账单
        for (int i = 0; i < order.getContractMonth(); i++) {
            ContractBill contractBill = new ContractBill();
            contractBill.setIssueNumber(i + 1);
            contractBill.setBillType(BillType.RENT.getCode());
            contractBill.setBillAmount(order.getRent());
            contractBill.setAmountReceivable(order.getRent());
            contractBill.setAmountDiscount(BigDecimal.ZERO);
            contractBill.setAmountReality(BigDecimal.ZERO);
            contractBill.setAmountReceived(BigDecimal.ZERO);
            contractBill.setBillStatus(BillStatus.UNPAID.getCode());
            contractBill.setAdvanceStatus(AuditStatus.N.getCode());
            // 到期时间根据订单开始时间加上i+1个月
            contractBill.setExpiresTime(DateUtil.offsetMonth(order.getStartDate(), i + 1));
            contractBill.setOverdue(AuditStatus.N.getCode());
            contractBill.setOrderId(order.getId());
            contractBill.setUserId(order.getUserId());
            contractBill.setLessorUserId(order.getLessorUserId());
            boolean b = baseMapper.insert(contractBill) > 0;
            if (!b) {
                throw new ServiceException("创建租金账单过程失败");
            }
        }
        return Boolean.TRUE;
    }

    /**
     * 更新订单的CapitalId
     *
     * @param order 订单
     * @return
     */
    @Override
    public Boolean updateCapitalIdByOrderId(Order order) {
        // 根据订单Id查询账单列表
        List<ContractBill> contractBillList = queryByOrderId(order.getId());
        // 遍历账单列表插入资方id
        for (ContractBill contractBill : contractBillList) {
            contractBill.setCapitalUserId(order.getCapitalUserId());
        }
        return baseMapper.updateBatchById(contractBillList);
    }

    public List<ContractBill> queryByOrderId(Long id) {
        return baseMapper.selectList(new LambdaQueryWrapper<ContractBill>().eq(ContractBill::getOrderId, id));
    }

    /**
     * 处理前六条未垫资租金账单
     *
     * @param orderId
     * @return
     */
    @Override
    public Boolean handle6ListByOrderId(Long orderId) {
        // 根据orderID获取用户租金账单前六条更改信息
        if (orderId == null) {
            throw new ServiceException("订单ID不能为空");
        }
        LambdaQueryWrapper<ContractBill> lqw = new LambdaQueryWrapper<>();
        // 根据orderId查询最近账单，按照issue_number排序
        lqw.eq(ContractBill::getOrderId, orderId)
            .eq(ContractBill::getAdvanceStatus, AuditStatus.N.getCode())
            .orderByAsc(ContractBill::getIssueNumber)
            .last("limit 6");
        List<ContractBill> contractBillList = baseMapper.selectList(lqw);
        log.info("查询最近6条待处理账单：{}", contractBillList);
        if (CollUtil.isEmpty(contractBillList)) {
            throw new ServiceException("此订单下没有租客租金账单或者已经垫资完成！");
        }
        for (ContractBill contractBill : contractBillList) {
            contractBill.setAdvanceStatus(AuditStatus.Y.getCode());
            boolean b = baseMapper.updateById(contractBill) > 0;
            if (!b) {
                throw new ServiceException("更新用户租金账单状态异常！");
            }
        }
        return Boolean.TRUE;
    }

    /**
     * 判断用户未支付租金账单是否逾期
     *
     * @param userId 用户id
     * @return 是否逾期
     */
    @Override
    public Boolean isOverdue(Long userId) {
        LambdaQueryWrapper<ContractBill> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ContractBill::getUserId, userId)
            // 到期时间在当前时间之前
            .lt(ContractBill::getExpiresTime, DateTime.now())
            // 订单状态为待付款或者部分付款
            .in(ContractBill::getBillStatus, BillStatus.UNPAID.getCode(), BillStatus.PART.getCode());

        List<ContractBill> list = baseMapper.selectList(lqw);
        if (CollUtil.isNotEmpty(list)) {
            for (ContractBill bill : list) {
                bill.setOverdue(AuditStatus.Y.getCode());
                baseMapper.updateById(bill);
            }
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    /**
     * 根据订单ID查询账单列表
     *
     * @param orderIds 订单IDS
     * @param begin    开始时间
     * @param end      结束时间
     * @return
     */
    @Override
    public List<ContractBillVo> selectListByOrderIds(List<Long> orderIds, DateTime begin, DateTime end) {
        return baseMapper.selectVoList(new LambdaQueryWrapper<ContractBill>()
            .in(ContractBill::getOrderId, orderIds)
            // 当begin和end都不为空时，查询指定时间段内的账单
            .between(begin != null && end != null, ContractBill::getExpiresTime, begin, end)
            .in(begin != null && end != null, ContractBill::getBillStatus, BillStatus.UNPAID.getCode(), BillStatus.PART.getCode())
            .eq(begin != null && end != null, ContractBill::getOverdue, AuditStatus.N.getCode())
            .eq(begin == null && end == null, ContractBill::getBillType, BillType.OVERDUE_RETURN.getCode()));
    }
}
